// 配送
package seller

import (
    "fmt"
	"dhfshop/utils/helper"
	"dhfshop/utils/math"
	"errors"
	"strings"
	"strconv"
	"encoding/json"
	"dhfshop/models"
	"github.com/astaxie/beego/orm"
	"github.com/astaxie/beego"
)
// 运费模板
// 模板表
type FareTemplate struct {
    FareId          int64    `orm:"column(fare_id);pk;auto" json:"fare_id" form:"fare_id"`
    PartnerId       int64      `orm:"column(partner_id)" json:"partner_id" form:"partner_id"`  //商家ID
	StoreId         int64      `orm:"column(store_id)" json:"store_id" form:"store_id"`        //门店ID
	Name            string   `orm:"column(name);size(32);null" json:"name" form:"name"`      //模板名称 
	IsInclPostage   int      `orm:"column(is_incl_postage);default(2)" json:"is_incl_postage" form:"is_incl_postage"` //是否包邮:1=是 2=否
	ValuationModel  int      `orm:"column(valuation_model)" json:"valuation_model" form:"valuation_model"` //计价方式(1:按件 2:按重量 3:按体积)
    Remark          string   `orm:"column(remark);size(128);null" json:"remark" form:"remark"`             /* 备注  */  
    CarryModes  []*CarryMode `orm:"reverse(many)" json:"carry_modes,omitempty" form:"-"`       /* 反向关系,用来表述ORM存储关系和组合JSON输出*/
    CarryModesJson  string   `orm:"-" json:"-" form:"carry_modes,omitempty"`                   /* 临时存放上传的订单sku*/	    
}

//运送方式表, region存储的是ID
type CarryMode struct {
    Id           int64         `orm:"column(id);pk;auto" json:"id" form:"id"`
	Fare         *FareTemplate `orm:"column(fare_id);rel(fk);on_delete(cascade)" json:"-" `     //模板表外键
    FareId       int64         `orm:"-" json:"fare_id"`
	Region       string        `orm:"column(region);type(text);null" json:"region,omitempty"`  // 运送地区(存id,以','分隔)
	FirstPiece   float32       `orm:"column(first_piece);digits(12);decimals(2)" json:"first_piece,omitempty"`    // 首件数量
	FirstAmount  float32       `orm:"column(first_amount);digits(12);decimals(2)" json:"first_amount,omitempty"`  // 首费
	SecondPiece  float32       `orm:"column(second_piece);digits(12);decimals(2)" json:"second_piece,omitempty"`   // 续件
	SecondAmount float32       `orm:"column(second_amount);digits(12);decimals(2)" json:"second_amount,omitempty"`  // 续费
	CarryWay     string        `orm:"column(carry_way)" json:"carry_way,omitempty"`  //运送方式（express=快递 ems=EMS post=平邮）
	//IsDefault  bool          `orm:"column(is_default)" json:"is_default"`          //是否默认的运送方式 
	FId          int64         `orm:"column(f_id);default(0)" json:"f_id" form:"f_id"`
	//对应主店的运送方式主键
}

func init() {
    orm.RegisterModel(new(FareTemplate),new(CarryMode))   
}

func AddFareTemplate(m *FareTemplate) (id int64, err error) {
	o := models.GetDefaultDB()
	id, err = o.Insert(m)
	return
}

func AddCarryMode(m *CarryMode) (id int64, err error) {
	o := models.GetDefaultDB()
	id, err = o.Insert(m)
	return
}

func AddMultiModels(list []*CarryMode) (err error) {
	size := len(list)
	o := models.GetDefaultDB()
	if size > 1 {
	    _, err = o.InsertMulti(size, list) 
	}else{
	   _, err = o.Insert(list[0])
	}
	return
}

func GetFareTemplateById(id int64) (v *FareTemplate, err error) {
	o := models.GetDefaultDB()
	v = &FareTemplate{FareId: id}
	if err = o.Read(v); err == nil {
		return v, nil
	}
	return nil, err
}

func GetFareTemplateByRemark(remark string)(templates []*FareTemplate, err error) {
	templates = []*FareTemplate{}
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(FareTemplate)).Filter("remark",remark)
	_, err = qs.All(&templates)
	return
}

func GetCarryModeById(id int64) (v *CarryMode, err error) {
	o := models.GetDefaultDB()
	v = &CarryMode{Id: id}
	if err = o.Read(v); err == nil {
		return v, nil
	}
	return nil, err
}

func GetCarryModeByFareIdAndFId(fareId, fId int64) (carryMode *CarryMode, err error) {
	carryMode = &CarryMode{}
	o := models.GetDefaultDB()
	err = o.QueryTable(new(CarryMode)).Filter("fare_id",fareId).Filter("f_id",fId).One(carryMode)
	return
}

func GetAllFareTemplates(storeIds []int64,query map[string]string, fields []string, order_by map[string]string, 
    offset int, limit int) ([]*FareTemplate, int64,error) {
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(FareTemplate))
	for k, v := range query {
		k = strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(k, v)
	}
	if len(storeIds) > 0 {
        qs = qs.Filter("store_id__in", storeIds)
    }
	cnt, err := qs.Count()
	// order by:
	var sortFields []string
	for k, v := range order_by {
		orderby := ""
		if v == "desc" {
			orderby = "-" + k
		} else if v == "asc" {
			orderby = k
		} else {
			return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
		}
		sortFields = append(sortFields, orderby)
	}
	qs = qs.OrderBy(sortFields...)
	var lists []*FareTemplate
	if _, err := qs.Limit(limit, offset).All(&lists, fields...); err == nil {
	    for _, temp := range lists {
			o.LoadRelated(temp, "CarryModes")
			for _, mode := range temp.CarryModes { 
                mode.FareId = mode.Fare.FareId			
			    mode.Fare = nil
			}
	    }
		return lists, cnt, nil
	}
	return nil, 0, err
}

func UpdateFareTemplateById(m *FareTemplate, k ...string) (err error) {
	o := models.GetDefaultDB()
	v := FareTemplate{FareId: m.FareId}
	if err = o.Read(&v); err != nil {
		return err
	}else{
	    if _, err = o.Update(m, k...); err != nil {
		   return err
		}
	}
	return nil
}

func UpdateCarryMode(m *CarryMode, k ...string) (err error) {
       o := models.GetDefaultDB()
	v := CarryMode{Id: m.Id}
	if err = o.Read(&v); err == nil {
		_, err = o.Update(m, k...)
		return err
	}
	return
}

func DeleteFareTemplate(id int64) (err error) {
	o := models.GetDefaultDB()
	v := FareTemplate{FareId: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}

func DeleteCarryMode(id int64) (err error) {
	o := models.GetDefaultDB()
	v := CarryMode{Id: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}

func GetCarryModes(fareId, regionId int64) (carryModes []*CarryMode, err error) {
	sql := fmt.Sprintf(`select * from carry_mode where fare_id = %d and (find_in_set(%d,region) or find_in_set(1,region))`, fareId, regionId)
	o := models.GetDefaultDB()					
	_, err = o.Raw(sql).QueryRows(&carryModes)
	return
}

func FilterCarryModes(srcCarryModes []*CarryMode) ([]int64, error) {
	m := map[string]string{}
	dstModeIds := []int64{}
	for _, mode := range srcCarryModes {
		if v, ok := m[mode.CarryWay]; ok {
		    if len(mode.Region) < 3 && len(v) > 3 {
			   continue
			}
			m[mode.CarryWay] = mode.Region
		}else{
		    m[mode.CarryWay] = mode.Region
		}
	}
	for _, mode := range srcCarryModes {
		if v, ok := m[mode.CarryWay]; ok && v == mode.Region {
		    dstModeIds = append(dstModeIds, mode.Id)
		}
	}
	return dstModeIds, nil
}

type FareModel struct {
	ItemId     string  `json:"item_id"`
	FareId     int64   `json:"fare_id"`
	TypeId     int     `json:"type_id"`  //计费方式：1按件;2按重;3按体积
	Quantity   int     `json:"quantity"`
	Weight     float32 `json:"weight"`
	Volume     float32 `json:"volume"`
	//补充数据
	ExpressFee float32 `json:"express_fee"` //快递运输费用
	EmsFee     float32 `json:"ems_fee"`     //EMS运输费用
	PostFee    float32 `json:"post_fee"`    //POST运输费用
}

type CarryFeeCacheData struct {
    OrderIdStr      string        `json:"order_id"`
	DefaultPostWay  string        `json:"default_postway"`
	ItemFareModels  []*FareModel  `json:"fare_models"`
}

//计算每种商品总件数，总重量，总体积等
func PushItem(fareModels []*FareModel,itemId string,fareId int64,typeId,num int,weight,volume float32)([]*FareModel,error){
   ok := false
   for _, value := range fareModels {
	  if itemId == value.ItemId {
		   if typeId == 1 {
			  value.Quantity = value.Quantity + num
		   } else if typeId == 2 {
			  value.Weight = value.Weight + weight 
		   } else {
			  value.Volume = value.Volume + volume
		   }
		   ok = true
	  }
   }
   if !ok {
       model := new(FareModel)
	   model.ItemId = itemId
	   model.FareId = fareId
	   model.TypeId = typeId
	   if typeId == 1 {
	      model.Quantity = num
	   } else if typeId == 2 {
	      model.Weight = math.MultiTotalPrice(weight, int32(num))
	   } else {
	      model.Volume = math.MultiTotalPrice(volume, int32(num))
	   }
       fareModels = append(fareModels, model)
   }
   return fareModels,nil
}

func GetPostFeeByFirst(piece,firstPiece,secondPiece,firstAmount,secondAmount float32) float32 {
	var postFee float32 = 0
	if piece <= firstPiece {
		postFee = firstAmount
	}else{
		postFee = firstAmount
		leftQuantity := piece - firstPiece
		postFee = postFee + math.CeilPostFee(leftQuantity, secondPiece, secondAmount)
	}
	return postFee
}

func GetPostFeeBySecond(piece,secondPiece,secondAmount float32) float32 {
	var postFee float32 = 0
	postFee = math.CeilPostFee(piece, secondPiece, secondAmount)
	return postFee
}

type FareInfo struct {
	FareId       int64    `json:"fare_id"`
	TypeId       int      `json:"type_id"`        //计费方式：1按件;2按重;3按体积
	Quantity     int      `json:"quantity"`       //总数量
	Weight       float32  `json:"weight"`         //总重量
	Volume       float32  `json:"volume"`         //总体积
	ExpressFee   float32  `json:"express_fee"`    //快递总费用
	EmsFee       float32  `json:"ems_fee"`        //EMS总费用
	PostFee      float32  `json:"post_fee"`       //平邮总费用
	IsMaxExpress bool     `json:"is_max_express"` //是否快递首费最大模板
	IsMaxEms     bool     `json:"is_max_ems"`     //是否EMS首费最大模板
	IsMaxPost    bool     `json:"is_max_post"`    //是否平邮首费最大模板
}

func GetPostAges(fareModels []*FareModel,regionId int64)(string,float32,[]*FareModel,error) {
	var (
		fareListMap = make(map[int64]*FareInfo)
	)
	for _, value := range fareModels{
	    b, _ := json.Marshal(value)
		beego.Debug("FareModel 1:", string(b))
	}
	//按模板合并商品量
	for _, value := range fareModels {
		if _, ok := fareListMap[value.FareId]; !ok {
		   pFareInfo := new(FareInfo)
		   pFareInfo.FareId = value.FareId 
		   pFareInfo.TypeId = value.TypeId
		   if value.TypeId == 1{
		      pFareInfo.Quantity = value.Quantity
		   }else if value.TypeId == 2 {
		      pFareInfo.Weight = value.Weight
		   }else{
		   }
		   fareListMap[value.FareId] = pFareInfo
		}else{
		   pFareInfo := fareListMap[value.FareId]
		   pFareInfo.FareId = value.FareId 
		   pFareInfo.TypeId = value.TypeId
		   if value.TypeId == 1{
		      pFareInfo.Quantity = pFareInfo.Quantity + value.Quantity
		   }else if value.TypeId == 2 {
		      pFareInfo.Weight = pFareInfo.Weight + value.Weight
		   }else{
		   }
		   fareListMap[value.FareId] = pFareInfo
		}
      }
	  
	  for _, value := range fareListMap{
		 b, _ := json.Marshal(value)
		 beego.Debug("FareModel 2:", string(b))
	  }
	  
      //查找最大首费模板
	  var (
	     maxExpreeFee    float32 = 0.0
		 maxEmsFee       float32 = 0.0
		 maxPostFee      float32 = 0.0
		 maxExpreeFareId int64
		 maxEmsFareId    int64
		 maxPostFareId   int64
	  )
	  for key, _:= range fareListMap {
	      // 由于约定：有特定地区模板的就不能使用全国的，因此需要对两者都存在的数据去重
		  if modes, err := GetCarryModes(key, regionId); err == nil && len(modes) > 0 {
			validIds, _ := FilterCarryModes(modes)
			for _, mode := range modes {
				if _, err := helper.Contains(mode.Id, validIds); err != nil {
					continue
				}
				if mode.CarryWay == "express" {
					if mode.FirstAmount >= maxExpreeFee {
					   maxExpreeFee = mode.FirstAmount
					   maxExpreeFareId = key
					}
				}else if mode.CarryWay == "ems" {
					if mode.FirstAmount >= maxEmsFee {
					   maxEmsFee = mode.FirstAmount
					   maxEmsFareId = key
					}
				}else if mode.CarryWay == "post" {
					if mode.FirstAmount >= maxPostFee {
					   maxPostFee = mode.FirstAmount
					   maxPostFareId = key
					}
				}else{

				}
			}
		} else {
			return "",0.0,nil,errors.New("not found favorite carry mode")
		}
	  }
	  beego.Debug("FareModel 3 maxExpreeFee,maxEmsFee,maxPostFee:",maxExpreeFee,maxEmsFee,maxPostFee)
	  
	   beego.Debug("FareModel 4 maxExpreeFareId,maxEmsFareId,maxPostFareId:",maxExpreeFareId,maxEmsFareId,maxPostFareId)
	   
	  //标记最大快递首费模板
	  if maxExpreeFareId > 0 {
	     value, _:= fareListMap[maxExpreeFareId]
		 value.IsMaxExpress = true
		 fareListMap[maxExpreeFareId] = value
	  }
	  //标记最大EMS首费模板
	  if maxEmsFareId > 0 {
	     value, _:= fareListMap[maxEmsFareId]
		 value.IsMaxEms = true
		 fareListMap[maxEmsFareId] = value
	  }
	  //标记最大平邮首费模板
	  if maxPostFareId > 0 {
	     value, _:= fareListMap[maxPostFareId]
		 value.IsMaxPost = true
		 fareListMap[maxPostFareId] = value
	  }
	  //按照模板计算总运费
	  for key, value := range fareListMap {
	      // 由于约定：有特定地区模板的就不能使用全国的，因此需要对两者都存在的数据去重
		  if modes, err := GetCarryModes(key, regionId); err == nil && len(modes) > 0 {
			validIds, _ := FilterCarryModes(modes)
			for _, mode := range modes {
				if _, err := helper.Contains(mode.Id, validIds); err != nil {
					continue
				}
				if mode.CarryWay == "express" {
					if value.TypeId == 1 {
					    if value.IsMaxExpress {
						   value.ExpressFee = GetPostFeeByFirst(float32(value.Quantity),mode.FirstPiece,mode.SecondPiece,mode.FirstAmount,mode.SecondAmount)
						}else{
						   value.ExpressFee = GetPostFeeBySecond(float32(value.Quantity),mode.SecondPiece,mode.SecondAmount)
						}
					}else if value.TypeId == 2 {
					    if value.IsMaxExpress {
						   value.ExpressFee = GetPostFeeByFirst(value.Weight,mode.FirstPiece,mode.SecondPiece,mode.FirstAmount,mode.SecondAmount)
						}else{
						   value.ExpressFee = GetPostFeeBySecond(value.Weight,mode.SecondPiece,mode.SecondAmount)
						}
					}else{
					}	
				}else if mode.CarryWay == "ems" {
					if value.TypeId == 1 {
					    if value.IsMaxEms {
						   value.EmsFee = GetPostFeeByFirst(float32(value.Quantity),mode.FirstPiece,mode.SecondPiece,mode.FirstAmount,mode.SecondAmount)
						}else{
						   value.EmsFee = GetPostFeeBySecond(float32(value.Quantity),mode.SecondPiece,mode.SecondAmount)
						}
					}else if value.TypeId == 2 {
					    if value.IsMaxEms {
						   value.EmsFee = GetPostFeeByFirst(value.Weight,mode.FirstPiece,mode.SecondPiece,mode.FirstAmount,mode.SecondAmount)
						}else{
						   value.EmsFee = GetPostFeeBySecond(value.Weight,mode.SecondPiece,mode.SecondAmount)
						}
					}else{
					}
				}else if mode.CarryWay == "post" {
				    if value.TypeId == 1 {
					    if value.IsMaxPost {
						   value.PostFee = GetPostFeeByFirst(float32(value.Quantity),mode.FirstPiece,mode.SecondPiece,mode.FirstAmount,mode.SecondAmount)
						}else{
						   value.PostFee = GetPostFeeBySecond(float32(value.Quantity),mode.SecondPiece,mode.SecondAmount)
						}
					}else if value.TypeId == 2 {
					    if value.IsMaxPost {
						   value.PostFee = GetPostFeeByFirst(value.Weight,mode.FirstPiece,mode.SecondPiece,mode.FirstAmount,mode.SecondAmount)
						}else{
						   value.PostFee = GetPostFeeBySecond(value.Weight,mode.SecondPiece,mode.SecondAmount)
						}
					}else{
					}
				}else{
				}
			}
		} else {
			return "",0.0,nil,errors.New("not found favorite carry mode")
		}
	  }
	  for _, value := range fareListMap{
		 b, _ := json.Marshal(value)
		 beego.Debug("FareModel 5:", string(b))
	  }
	  //按照商品量分摊运费
	  var sumExpress float32 = 0.0
	  var sumEms float32 = 0.0
	  var sumPost float32 = 0.0
	  for key, value := range fareModels {
		v, _:= fareListMap[value.FareId]
		if key == (len(fareModels) - 1) {
		   value.ExpressFee = math.SubPrice(v.ExpressFee, sumExpress)
		   value.EmsFee = math.SubPrice(v.EmsFee, sumEms)
		   value.PostFee = math.SubPrice(v.PostFee, sumPost)
		}else{
			if value.TypeId == 1 {
			   value.ExpressFee = float32(value.Quantity)/float32(v.Quantity)*v.ExpressFee
			   value.ExpressFee = math.SubPrice(value.ExpressFee, 0.00)
			   value.EmsFee = float32(value.Quantity)/float32(v.Quantity)*v.EmsFee
			   value.EmsFee = math.SubPrice(value.EmsFee, 0.00)
			   value.PostFee = float32(value.Quantity)/float32(v.Quantity)*v.PostFee 
			   value.PostFee = math.SubPrice(value.PostFee, 0.00)
			}else if value.TypeId == 2 {
			   value.ExpressFee = value.Weight/v.Weight*v.ExpressFee
			   value.ExpressFee = math.SubPrice(value.ExpressFee, 0.00)
			   value.EmsFee = value.Weight/v.Weight*v.EmsFee
			   value.EmsFee = math.SubPrice(value.EmsFee, 0.00)
			   value.PostFee = value.Weight/v.Weight*v.PostFee
			   value.PostFee = math.SubPrice(value.PostFee, 0.00)
			}else{
			
			}
			sumExpress = sumExpress + value.ExpressFee
			sumEms = sumEms + value.EmsFee
			sumPost = sumPost + value.PostFee
		}
	}
	for _, value := range fareModels{
	    b, _ := json.Marshal(value)
		beego.Debug("FareModel 6:", string(b))
	}
	
	defaultPostWay, defaultPostFee, err := GetAllItemFees(fareModels)
	defaultPostFee = math.SubPrice(defaultPostFee, 0.00)
	beego.Debug("FareModel 7 defaultPostWay:",defaultPostWay,"defaultPostFee:",defaultPostFee)
	return defaultPostWay,defaultPostFee,fareModels, err
}

// 返回给前端的邮费详情
// modeId 1: 交集 2: 非交集
// post_details:{11111:{ems:22,express:33,sway:"ems"}}
func OutputCarryFee(defaultWay string, fareModels []*FareModel) *map[string]map[string]string {
	var itemPostFeeMap map[string]map[string]string = make(map[string]map[string]string) // map[item_id]map[carry_way]fee
	for _, value := range fareModels {
		if _, ok := itemPostFeeMap[value.ItemId]; !ok {      
			itemPostFeeMap[value.ItemId] = make(map[string]string)
		}
		if value.ExpressFee > 0 {
		   itemPostFeeMap[value.ItemId]["express"] = strconv.FormatFloat(float64(value.ExpressFee), 'f', -1, 32)
		}
		if value.EmsFee > 0 {
		   itemPostFeeMap[value.ItemId]["ems"] = strconv.FormatFloat(float64(value.EmsFee), 'f', -1, 32)
		}
		if value.PostFee > 0 {
		   itemPostFeeMap[value.ItemId]["post"] = strconv.FormatFloat(float64(value.PostFee), 'f', -1, 32)
		}
		itemPostFeeMap[value.ItemId]["sway"] = defaultWay	
	}
	return &itemPostFeeMap
}

func GetAllItemFees(fareModels []*FareModel)(string,float32,error) {
	var(
	    expressFee float32 = 0.0
	    emsFee float32 = 0.0
		postFee float32 = 0.0
	)
	for _, value := range fareModels {
		expressFee = expressFee + value.ExpressFee
		emsFee     = emsFee + value.EmsFee
		postFee    = postFee + value.PostFee
    }
	defaultPostFee := expressFee
	defaultPostWay := "express"
	if emsFee > 0 && emsFee < defaultPostFee {
	   defaultPostFee = emsFee
	   defaultPostWay = "ems"
	}
	if postFee > 0 && postFee < defaultPostFee {
	   defaultPostFee = postFee
	   defaultPostWay = "post"
	}
	if defaultPostFee < 0.01 {
	   if postFee < 0.01 {
	      defaultPostFee = emsFee
	      defaultPostWay = "ems"
	   }else{
	      defaultPostFee = postFee
	      defaultPostWay = "post"
	   }
	}
	defaultPostFee = math.MultiTotalPrice(defaultPostFee, 1)
	return defaultPostWay,defaultPostFee,nil
}

func GetOtherPostFee(sway string, value *FareModel) float32 {
	var carryFee float32 = 0.0
	if sway == "express" {
	   carryFee = value.ExpressFee
	}
	if sway == "ems" {
	   carryFee = value.EmsFee
	}
	if sway == "post" {
	   carryFee = value.PostFee
	}
	return carryFee
}
